【AsusWrt】华硕梅林固件路由器信息监控之【命令行py脚本】 |
您所在的位置:网站首页 › 华硕 梅林 ssh connection refused › 【AsusWrt】华硕梅林固件路由器信息监控之【命令行py脚本】 |
【AsusWrt】华硕梅林固件路由器信息监控之【命令行py脚本实现法】
Sensor.Command_line Python脚本 经验分享 萌新探索系列 憋说话,先发图。 登录/注册后可看大图 360截图16450708758970.png (18.78 KB, 下载次数: 0) 下载附件 2018-4-24 10:09 上传 更新 4月27日: 登录/注册后可看大图微信截图_20180427020412.png (2.77 KB, 下载次数: 0) 下载附件 2018-4-27 02:05 上传 计划新增用于不可描述之事的命令: _CMD_SSFS = 'nvram get ss_foreign_state' _CMD_SSCS = 'nvram get ss_china_state' 前言最近,萌新楼主拜读了囧帅大大@Jone的神贴:梅林路由器CPU和无线芯片温度接入Home Assistant之后,有所启发。结合前期自己利用HA对路由器等网络设备进行信息监控的一些探索和经验,本楼主写了这段在HA上实现的AsusWrt固件路由器信息监控的Py脚本,并配以相应的配置文件,希望对您有所帮助。 想法编写这个脚本的初衷纯粹是为了把玩,即用其它方法实现囧帅大大帖子中对路由器信息的监控,再就是为以后编写关于AsusWrt的自定义组件积累经验。 有兴趣的同学可以先看看囧帅大大的方法,思路很棒,利用路由器的JFFS存储并自动执行脚本获取路由器数据,然后路由器将这些信息以JSON序列主动向HA的API进行发送。囧帅大大的方法很妙,最关键是不占HA资源,HA不需要向路由器请求,只需要接收数据。 然后我这个方法,其实是土办法,在HA加载一个命令行传感器,命令行传感器执行的是一个py脚本,在脚本中通过SSH登录到路由器并通过运行命令行获得数据,数据经过正则表达式等的修正,成为有效载荷,作为HA命令行传感器的state传回到HA平台。 这种方法虽然土,但一样有效,因为只用了一个命令行传感器执行脚本,避免了SSH疯狂并发工作导致路由器Ban掉HA系统ip的窘境,经过在群晖docker下的HA上实测,工作还算稳定,虽然脚本很单薄,没有太多的异常处置,但是目前还没有报过任何错误。然后就是在本方法中路由器只需要打开SSH,JFFS之类不用打开(最关键可能就是这点,满足了我私人的需求,因为我就是纠结地银,我不想单独为监控信息这种类型的功能打开JFFS,这方法对HA来说很妙,但对路由器来说意义却不大...而且感觉这种方法的远景建设性程度比较受限,总之我个人不足以说服自己)。 那么这个方法是好方法么,当然不是啊!楼主我自己也说了,本文这种方法只能用于把玩把玩,最好的办法还是编写自定义组件!(当然,没有组件之前随便用用并没有啥问题。) 本文方法的局限性有以下几点: 这种方法中提取芯片温度的手段略显粗糙,路由器固件自身是通过ioctl函数获取温度值的,下步看看怎么改进。 本文中没有提取上传下载量,这属于应该被提取的信息,下步改进。 通过这种方法传回来的state值,最大只能255字节,HA你妹的设定,我本来是准备直接传回一整段JSON序列的,但是采集了那么一点点数据,一看JSON数据大小就要500多字节,只能砍掉字典的键、保留值并做成列表形式(砍完正好250字节左右,我地个神),字符串传回来,最后重建列表提取信息。这个问题单单在脚本中无法解决,脚本毕竟是脚本,毕竟是受限于HA的,当然破解方法也不是没有,比如在脚本中把JSON序列保存为HA本地文件,再通过另外的HA传感器把文件读出来...... 使用这种方法虽然避免了SSH连接请求短时间疯狂并发的情况,但是每一次运行命令行完毕必然导致SSH关闭,下次请求再打开,我现在设定是每30秒运行一次命令行脚本,在路由器上看到的日志就是ssh不停地打开和关闭......这个问题还是要通过编写自定义组件来解决吧。 最后就是这种方法其实和系统自带的device_tracker.asuswrt组件有很多方法是相通的重复的,未来编写自定义组件应该考虑合并掉DeviceTrack.AsusWrt的功能,有时觉得这个track组件真心不好用,看看能否改进和增加新功能,比如利用wl rssi命令,通过连接无线网络设备的MAC地址,反查设备的信号强度,以此作为tracker等功能判定的依据。不过前几天看了下device_tracker这个组件,核心文件__init__.py就比sensor组件的要复杂得多,处处涉及异步编程,光配置平台就有4种方法:async_get_scanner、get_scanner、async_setup_scanner、setup_scanner,让萌新我看得云里雾里......哎,我是一月前连python是啥都不知道,Linux一个命令也不懂的人啊,正要命。 文件 asuswrt_mirukuteii.py建议在HA系统根目录下找个地方放这个文件,比如我新建了一个script文件夹,专门放脚本文件。 ############################################# # AsusWrt_MRKT # ############################################# # 本脚本为收集AsusWrt固件的路由器信息而编写 # # 实现方法:在HA中调用命令行传感器运行本脚本 # # 本脚本通过SSH连接AsusWrtRouter, # # 取得相应信息并返回至命令行传感器。 # # 作者:Mirukuteii@hasssbian 2018-4-25 # ############################################# import re REQUIREMENTS = ['pexpect==4.0.1'] #SSH连接参数,请按路由器实际填写 _RT_IP = 'XX.XX.XX.XX' _RT_PT = 'XXXX' _RT_USR = 'XXXXXXX' #SSH密码和证书两种方式2选1,空着的那个直接填''即可 _RT_SSHPWD = 'XXXXXXXX' _RT_SSHKEYPATH = '/XX/.ssh/id_rsa' #命令行组 _CMD_NAME = 'nvram get computer_name' _CMD_WANIP = 'nvram get wan_ipaddr' _CMD_LANIP = 'nvram get lan_ipaddr' _CMD_MAC = 'nvram get lan_hwaddr' _CMD_UPTIME = 'uptime' _CMD_CPUTEMP = "cat /proc/dmu/temperature |sed -e 's/[^0-9]//g'" _CMD_24GTEMP = "wl -i eth1 phy_tempsense |awk '{print $1 / 2 + 20}'" _CMD_5GTEMP = "wl -i eth2 phy_tempsense |awk '{print $1 / 2 + 20}'" _CMD_24GTXPWR = 'wl -i eth1 txpwr_target_max' _CMD_5GTXPWR = 'wl -i eth2 txpwr_target_max' _CMD_MEM = 'top -n 1 -b |grep ^Mem' #_CMD_RSSI = wl -i eth1 rssi MAC #正则公式组 _REGEX_NAME = re.compile( r'(?P;router_name;(.+))\s+') _REGEX_WANIP = re.compile( r'(?P;wan_ip;(.+))\s+') _REGEX_LANIP = re.compile( r'(?P;lan_ip;(.+))\s+') _REGEX_MAC = re.compile( r'(?P;mac;(.+))\s+') _REGEX_UPTIME = re.compile( r'\s' + r'(?P;router_nowtime;(.+))\sup\s' + r'(?P;router_uptime;(.+)),\s+load.+:\s' + r'(?P;cpu_load;(.+))\s+') _REGEX_CPUTEMP = re.compile( r'(?P;cpu_temp;(\d+))\s+') _REGEX_24GTEMP = re.compile( r'(?P;wl24G_temp;(\d+))') _REGEX_5GTEMP = re.compile( r'(?P;wl5G_temp;(\d+))') _REGEX_24GTXPWR = re.compile( r'.+:\s+' + r'(?P;wl24G_txpwr;(\d.+))\s') _REGEX_5GTXPWR = re.compile( r'.+:\s+' + r'(?P;wl5G_txpwr;(\d.+))\s') _REGEX_MEM = re.compile( r'Mem:\s' + r'(?P;mem_used;(\d+K))\sused,\s' + r'(?P;mem_free;(\d+K))\sfree,\s' + r'(?P;mem_shrd;(\d+K))\sshrd,\s' + r'(?P;mem_buff;(\d+K))\sbuff,\s' + r'(?P;mem_cached;(\d+K))\s.+') #定义类:_Connection class _Connection: def __init__(self): self._connected = False @property def connected(self): """Return connection state.""" return self._connected def connect(self): """Mark current connection state as connected.""" self._connected = True def disconnect(self): """Mark current connection state as disconnected.""" self._connected = False #定义类:SshConnection,继承_Connection类 class SshConnection(_Connection): """Maintains an SSH connection to an ASUS-WRT router.""" def __init__(self, host, port, username, password, ssh_key): """Initialize the SSH connection properties.""" super().__init__() self._ssh = None self._host = host self._port = port self._username = username self._password = password self._ssh_key = ssh_key def run_command(self, command): """Run commands through an SSH connection. Connect to the SSH server if not currently connected, otherwise use the existing connection. """ from pexpect import pxssh, exceptions try: if not self.connected: self.connect() self._ssh.sendline(command) self._ssh.prompt() lines = self._ssh.before.split(b'\n')[1:-1] return [line.decode('utf-8') for line in lines] except exceptions.EOF as err: #_LOGGER.error("Connection refused. %s", self._ssh.before) self.disconnect() return None except pxssh.ExceptionPxssh as err: #_LOGGER.error("Unexpected SSH error: %s", err) self.disconnect() return None except AssertionError as err: #_LOGGER.error("Connection to router unavailable: %s", err) self.disconnect() return None def connect(self): """Connect to the ASUS-WRT SSH server.""" from pexpect import pxssh self._ssh = pxssh.pxssh() if self._ssh_key: self._ssh.login(self._host, self._username, quiet=False, ssh_key=self._ssh_key, port=self._port) else: self._ssh.login(self._host, self._username, quiet=False, password=self._password, port=self._port) super().connect() def disconnect(self): \ # pylint: disable=broad-except """Disconnect the current SSH connection.""" try: self._ssh.logout() except Exception: pass finally: self._ssh = None super().disconnect() #定义函数_parse_lines(),从命令行返回值中按正则公式提取信息,返回为一个列表 def _parse_lines(lines, regex): """Parse the lines using the given regular expression. If a line can't be parsed it is logged and skipped in the output. """ results = [] for line in lines: match = regex.search(line) if not match: #_LOGGER.debug("Could not parse row: %s", line) continue results.append(match.groupdict()) return results #定义函数get_data_by(),执行命令行语句cmd_line; #并将返回值交给函数_parse_lines(); #按照正则公式regex_rule提取信息,作为本函数返回值. def get_data_by(connection, cmd_line, regex_rule): lines = connection.run_command(cmd_line) if not lines: return {} result = _parse_lines(lines, regex_rule) data = {} for element in result: data.update(element) return data #定义函数get_asuswrt_data,完成打开SSH连接并提取并打印数据的整个过程 def get_asuswrt_data(connection): asuswrt_data = {} asuswrt_data.update(get_data_by(connection, _CMD_WANIP, _REGEX_WANIP)) asuswrt_data.update(get_data_by(connection, _CMD_LANIP, _REGEX_LANIP)) asuswrt_data.update(get_data_by(connection, _CMD_MAC, _REGEX_MAC)) asuswrt_data.update(get_data_by(connection, _CMD_UPTIME, _REGEX_UPTIME)) asuswrt_data.update(get_data_by(connection, _CMD_CPUTEMP, _REGEX_CPUTEMP)) asuswrt_data.update(get_data_by(connection, _CMD_24GTEMP, _REGEX_24GTEMP)) asuswrt_data.update(get_data_by(connection, _CMD_5GTEMP, _REGEX_5GTEMP)) asuswrt_data.update(get_data_by(connection, _CMD_24GTXPWR, _REGEX_24GTXPWR)) asuswrt_data.update(get_data_by(connection, _CMD_5GTXPWR, _REGEX_5GTXPWR)) asuswrt_data.update(get_data_by(connection, _CMD_MEM, _REGEX_MEM)) dict = get_data_by(connection, _CMD_NAME, _REGEX_NAME) json_data = {'state': dict['router_name'], 'attributes': asuswrt_data} #本来这里可以str(json_data)返回了,怎奈HA只接受255B以内的数据 list_data = [dict['router_name']] for key in asuswrt_data: list_data.append(asuswrt_data[key]) str_data = ','.join(list_data) print(str_data) if __name__ == '__main__': asuswrt_connection = SshConnection(_RT_IP, _RT_PT, _RT_USR, _RT_SSHPWD, _RT_SSHKEYPATH) get_asuswrt_data(asuswrt_connection) asuswrt_mirukuteii.yaml建议放在packages文件夹中工作,也可修改添加到configuration.yaml中。 #AsusWrt路由器信息监控 sensor: - platform: command_line name: router #下面的路径按py文件的存放位置进行修改哦 command: "python3 /config/script/asuswrt_mirukuteii.py" scan_interval: 30 - platform: template sensors: router_name: icon_template: mdi:router-wireless friendly_name: "路由器名称" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[0] }} router_wanip: icon_template: mdi:ethernet friendly_name: "外网IP地址" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[1] }} router_lanip: icon_template: mdi:ethernet friendly_name: "内网IP地址" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[2] }} router_mac: icon_template: mdi:ethernet friendly_name: "路由器MAC地址" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[3] }} router_nowtime: icon_template: mdi:clock friendly_name: "路由器当前时间" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[4] }} router_uptime: icon_template: mdi:av-timer friendly_name: "路由器运行时间" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[5] }} {{ list[6] }} router_load_1min: icon_template: mdi:select-inverse friendly_name: "CPU平均负载:1分钟" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[7] }} router_load_5min: icon_template: mdi:select-inverse friendly_name: "CPU平均负载:5分钟" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[8] }} router_load_15min: icon_template: mdi:select-inverse friendly_name: "CPU平均负载:15分钟" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[9] }} router_cputemp: icon_template: mdi:thermometer friendly_name: "CPU温度" unit_of_measurement: '℃' value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[10] }} router_24gtemp: icon_template: mdi:thermometer friendly_name: "2.4G温度" unit_of_measurement: '℃' value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[11] }} router_5gtemp: icon_template: mdi:thermometer friendly_name: "5G温度" unit_of_measurement: '℃' value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[12] }} router_24gtxpwr: icon_template: mdi:wifi friendly_name: "2.4G天线发射功率" unit_of_measurement: 'dBm' value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[13] }} router_5gtxpwr: icon_template: mdi:wifi friendly_name: "5G天线发射功率" unit_of_measurement: 'dBm' value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {{ list[14] }} router_mem_used: icon_template: mdi:memory friendly_name: "已用内存" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {%- set mem = (list[15] |replace('K','') |int) -%} {%- if mem ;= 1024 -%} {%- set mem = mem / 1024 -%} {{ mem | round(1) }} MiB {%- else -%} {{ mem }} KiB {%- endif -%} router_mem_free: icon_template: mdi:memory friendly_name: "可用内存" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {%- set mem = (list[16] |replace('K','') |int) -%} {%- if mem ;= 1024 -%} {%- set mem = mem / 1024 -%} {{ mem | round(1) }} MiB {%- else -%} {{ mem }} KiB {%- endif -%} router_mem_shrd: icon_template: mdi:memory friendly_name: "共享内存" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {%- set mem = (list[17] |replace('K','') |int) -%} {%- if mem ;= 1024 -%} {%- set mem = mem / 1024 -%} {{ mem | round(1) }} MiB {%- else -%} {{ mem }} KiB {%- endif -%} router_mem_buff: icon_template: mdi:memory friendly_name: "磁盘缓存" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {%- set mem = (list[18] |replace('K','') |int) -%} {%- if mem ;= 1024 -%} {%- set mem = mem / 1024 -%} {{ mem | round(1) }} MiB {%- else -%} {{ mem }} KiB {%- endif -%} router_mem_cached: icon_template: mdi:memory friendly_name: "文件缓存" value_template: ;- {%- set list = states.sensor.router.state.split(',') -%} {%- set mem = (list[19] |replace('K','') |int) -%} {%- if mem ;=1024 -%} {%- set mem = mem / 1024 -%} {{ mem | round(1) }} MiB {%- else -%} {{ mem }} KiB {%- endif -%} group: routermon: name: 'ROUTER监控' view: no entities: - sensor.router_name - sensor.router_wanip - sensor.router_lanip - sensor.router_mac - sensor.router_nowtime - sensor.router_uptime - sensor.router_load_1min - sensor.router_load_5min - sensor.router_load_15min - sensor.router_cputemp - sensor.router_24gtemp - sensor.router_5gtemp - sensor.router_24gtxpwr - sensor.router_5gtxpwr - sensor.router_mem_used - sensor.router_mem_free - sensor.router_mem_shrd - sensor.router_mem_buff - sensor.router_mem_cached Groups.yaml在这个文件中某个你想要展示路由器监控情况项目的group的entities项中添加如下代码以实现图片中的状态卡片。 - group.routermon |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |